/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package estsapp.expertos;

import estsapp.entidades.DetalleOrden;
import estsapp.entidades.DetalleProducto;
import estsapp.entidades.DetalleReserva;
import estsapp.entidades.EstadoReservaMateriales;
import estsapp.entidades.MovimientoStock;
import estsapp.entidades.OrdenDeProduccion;
import estsapp.entidades.PedidoAProveedores;
import estsapp.entidades.Pieza;
import estsapp.entidades.ProductoTerminado;
import estsapp.entidades.ReservaMateriales;
import estsapp.entidades.Stock;
import estsapp.expertos.excepciones.ABMPedidoAProveedoresException;
import estsapp.expertos.excepciones.ExpertoStockException;
import java.sql.Date;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import persistencia.Conexion;
import persistencia.Fachada;
import persistencia.exceptions.ExcepcionFachada;

/**
 *
 * @author diego
 */
public class ExpertoStock implements Experto {

    /**
     * Disminuye la cantidad de stock de cada material segun orden de produccion
     *
     * @param orden
     */
    public void darDeBajaStockDeOrden(OrdenDeProduccion orden) throws ExpertoStockException {
        try {
            Conexion.getInstancia().iniciarTX();
            darDeBajaStockDeOrdenSinTX(orden);
            Conexion.getInstancia().confirmarTx();
        } catch (ExpertoStockException ex) {
            Conexion.getInstancia().deshacerTx();
            throw ex;
        }

    }

    public void darDeBajaStockDeOrdenSinTX(OrdenDeProduccion orden) throws ExpertoStockException {
        Date fecha = Fachada.getInstancia().getFecha();
        List<DetalleOrden> detallesOrden = orden.getListaDetalles();
        try {
            for (DetalleOrden detalleOrden : detallesOrden) {//Por cada detalle, recorre la estructura del producto
                ProductoTerminado producto = detalleOrden.getProductoTerminado();
                Long cantidadProducto = detalleOrden.getCantidad();
                for (DetalleProducto detalleProducto : producto.getDetalles()) {//recorre la estructura del producto y actualiza el stock de cada material
                    Float cantidadTotalMaterial = detalleProducto.getCantidad() * cantidadProducto;
                    Pieza pieza = detalleProducto.getPieza();
                    String observacion = "Producción realizada Según Orden Número: " + orden.getNumero();
                    darBajaStockSinTX(fecha, cantidadTotalMaterial, pieza, observacion);
                }
            }
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            throw new ExpertoStockException("Error al actualizar Stock orden de producción.");
        }

    }

    private void darBajaStockSinTX(Date fecha, Float cantidad, Pieza pieza, String observacion) throws ExcepcionFachada {
        MovimientoStock movimiento = new MovimientoStock();
        movimiento.setCantidad(cantidad);
        movimiento.setFecha(fecha);
        movimiento.setIsAlta(false);
        movimiento.setObservacion(observacion);

        pieza.getStock().getMovimientos().add(movimiento);
        Float cantidadTotal = pieza.getStock().getCantTotal() - cantidad;
        pieza.getStock().setCantTotal(cantidadTotal);

        Fachada.getInstancia().guardar(pieza);

    }

    /**
     * Produce un nuevo movimiento de stock para la baja de la Pieza ingresada
     *
     * @param fecha
     * @param cantidad
     * @param pieza
     * @throws ExpertoStockException
     */
    private void darBajaStock(Date fecha, Float cantidad, Pieza pieza, String observacion) throws ExpertoStockException {
        try {
            Conexion.getInstancia().iniciarTX();
            darBajaStockSinTX(fecha, cantidad, pieza, observacion);
            Conexion.getInstancia().confirmarTx();
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            throw new ExpertoStockException("Error al actualizar Stock: " + pieza.getNombre());
        }
    }

    public Float consultarCantidadDisponible(Pieza pieza) {

        Float cantidadReservada = consutarCantidadReservada(pieza);
        if (pieza.getStock() == null) {
            return 0f;
        }
        Float cantTotalStock = pieza.getStock().getCantTotal();
        Float cantDisponible = cantTotalStock - cantidadReservada;
        return cantDisponible;
    }

    /**
     * Calcula la cantidad reservada de una pieza
     *
     * @param pieza
     * @return
     */
    public Float consutarCantidadReservada(Pieza pieza) {

        EstadoReservaMateriales estado = buscarEstado("Esperando");
        Criteria esperando = Fachada.getInstancia().crearCriterio(ReservaMateriales.class);
        esperando.createCriteria("estados").add(Restrictions.eq("estado", estado));
        esperando.createCriteria("detalles").add(Restrictions.eq("material", pieza));
        List<ReservaMateriales> reservas = Fachada.getInstancia().buscar(ReservaMateriales.class, esperando);

        Float cantidadReservada = 0f;//
        for (ReservaMateriales reserva : reservas) {
            for (DetalleReserva detalle : reserva.getDetalles()) {
                if (detalle.getMaterial().equals(pieza)) {
                    cantidadReservada += detalle.getCantidad();
                }
            }
        }
        return cantidadReservada;
    }

    private EstadoReservaMateriales buscarEstado(String nombreEstado) {
        Criteria nombre = Fachada.getInstancia().crearCriterio(EstadoReservaMateriales.class);
        nombre.add(Restrictions.eq("nombre", nombreEstado));
        return (EstadoReservaMateriales) Fachada.getInstancia().buscar(EstadoReservaMateriales.class, nombre).get(0);
    }

    /**
     * crea el stock para una pieza con stock == null
     *
     * @param pieza
     */
    public void crearStock(Pieza pieza) throws ExcepcionFachada {
        pieza.setStock(new Stock());
        pieza.getStock().setCantTotal(0f);
        try {
            Conexion.getInstancia().iniciarTX();
            Fachada.getInstancia().guardar(pieza);
            Conexion.getInstancia().confirmarTx();
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            throw ex;
        }
    }

    public void aumentarStockPiezaSinTX(Pieza pieza, Float cantidad, String observacion, Date fecha) throws ExpertoStockException, ExcepcionFachada {
        MovimientoStock nuevoMovimiento = new MovimientoStock();
        nuevoMovimiento.setCantidad(cantidad);
        nuevoMovimiento.setFecha(fecha);
        nuevoMovimiento.setObservacion(observacion);
        nuevoMovimiento.setIsAlta(true);

        guardarMovimientoStockSinTX(pieza, nuevoMovimiento);

    }

    public void disminuirStockPiezaSinTX(Pieza pieza, Float cantidad, String observacion, Date fecha) throws ExpertoStockException, ExcepcionFachada {
        MovimientoStock nuevoMovimiento = new MovimientoStock();
        nuevoMovimiento.setCantidad(cantidad);
        nuevoMovimiento.setFecha(fecha);
        nuevoMovimiento.setObservacion(observacion);
        nuevoMovimiento.setIsAlta(false);

        guardarMovimientoStockSinTX(pieza, nuevoMovimiento);

    }

    private void guardarMovimientoStockSinTX(Pieza pieza, MovimientoStock movimiento) throws ExpertoStockException, ExcepcionFachada {
        validarMovimiento(movimiento);

        Float cant = pieza.getStock().getCantTotal();
        if (movimiento.isIsAlta()) {
            cant += movimiento.getCantidad();
        } else {
            cant -= movimiento.getCantidad();
            if (cant < 0) {
                throw new ExpertoStockException("La cantidad ingresada NO puede ser Mayor que la cantidad Disponible.");
            }
        }
        pieza.getStock().addMovimiento(movimiento);
        pieza.getStock().setCantTotal(cant);

        Fachada.getInstancia().guardar(pieza);

    }

    public void guardarMovimientoStock(Pieza pieza, MovimientoStock movimiento) throws ExpertoStockException, ExcepcionFachada {
        try {
            Conexion.getInstancia().iniciarTX();
            guardarMovimientoStockSinTX(pieza, movimiento);
            Conexion.getInstancia().confirmarTx();
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            throw ex;
        }
    }

    private void validarMovimiento(MovimientoStock movimiento) throws ExpertoStockException {
        if (movimiento.getCantidad() == -1) {
            throw new ExpertoStockException("Debe ingresar cantidad");
        }
        if (movimiento.getFecha() == null) {
            throw new ExpertoStockException("Debe seleccionar fecha");
        }
    }
    
    public void ingresarPedido(PedidoAProveedores pedido) throws ABMPedidoAProveedoresException, ExcepcionFachada{
        ExpertoABMPedidoAProveedores pedidosProveedor = (ExpertoABMPedidoAProveedores) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.ABM_PEDIDO_PROVEEDOR);
        pedidosProveedor.ingresarPedido(pedido);
    }
}
